package org.example;

import model.UserRequest;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;

public class TravelPlanner {
    private static List<Map<String, Object>> pois; // 统一的POI数据库
    private static final Scanner scanner = new Scanner(System.in);
    private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
    
    // 初始位置（假设酒店）
    private static final double[] HOTEL_LOCATION = {31.479650, 104.772529};
    
    // 启发式评分权重
    private static final double W1_POPULARITY = 10.0;
    private static final double W2_TRAVEL_TIME = 2.0;
    private static final double W3_TICKET_PRICE = 1.0;
    
    // 餐饮时间窗口
    private static final LocalTime LUNCH_START = LocalTime.of(11, 30);
    private static final LocalTime LUNCH_END = LocalTime.of(13, 0);
    private static final LocalTime DINNER_START = LocalTime.of(17, 30);
    private static final LocalTime DINNER_END = LocalTime.of(19, 0);

    public static void main(String[] args) {
        // 加载数据
        if (!loadData()) {
            System.out.println("❌ 加载数据失败，程序退出");
            return;
        }

        // 获取用户输入
        UserRequest userRequest = getUserInput();
        
        System.out.println("\n✅ 用户配置完成：");
        System.out.println("   天数: " + userRequest.getDays() + " 天");
        System.out.println("   必去: " + String.join(", ", userRequest.getMustVisit()));
        System.out.println("   禁止: " + String.join(", ", userRequest.getForbidden()));
        System.out.println("   时间: " + userRequest.getStartTime().format(timeFormatter) + 
                          " - " + userRequest.getEndTime().format(timeFormatter));
        System.out.println("=".repeat(60));

        // 生成并输出行程
        List<List<Map<String, Object>>> finalItinerary = generateItinerary(userRequest);
        printItinerary(finalItinerary);
    }

    private static boolean loadData() {
        try {
            System.out.println("🔍绵阳行程规划系统 - 第二阶段：启发式初始规划器");
            System.out.println("=".repeat(60));
            
            // 创建符合第二阶段要求的POI数据库
            pois = createPOIDatabase();
            
            System.out.println("✅ 数据加载成功");
            System.out.println("   POI总数: " + pois.size());
            System.out.println("   景点数量: " + pois.stream().filter(p -> "attraction".equals(p.get("type"))).count());
            System.out.println("   餐厅数量: " + pois.stream().filter(p -> "restaurant".equals(p.get("type"))).count());
            return true;
        } catch (Exception e) {
            System.out.println("❌ 加载数据失败: " + e.getMessage());
            return false;
        }
    }

    private static List<Map<String, Object>> createPOIDatabase() {
        List<Map<String, Object>> poiDatabase = new ArrayList<>();
        
        // 景点数据
        Map<String, Object> poi1 = new HashMap<>();
        poi1.put("id", "POI001");
        poi1.put("name", "绵阳方特东方神画");
        poi1.put("city", "绵阳");
        poi1.put("type", "attraction");
        poi1.put("coordinates", Map.of("lat", 31.479650, "lng", 104.772529));
        poi1.put("avg_visit_duration", 240); // 4小时
        poi1.put("opening_hours", Map.of("start", "09:00", "end", "22:00"));
        poi1.put("ticket_price", 280.0);
        poi1.put("popularity_score", 9.5);
        poiDatabase.add(poi1);
        
        Map<String, Object> poi2 = new HashMap<>();
        poi2.put("id", "POI002");
        poi2.put("name", "九皇山景区");
        poi2.put("city", "绵阳");
        poi2.put("type", "attraction");
        poi2.put("coordinates", Map.of("lat", 31.523456, "lng", 104.789012));
        poi2.put("avg_visit_duration", 180); // 3小时
        poi2.put("opening_hours", Map.of("start", "08:00", "end", "18:00"));
        poi2.put("ticket_price", 120.0);
        poi2.put("popularity_score", 8.0);
        poiDatabase.add(poi2);
        
        Map<String, Object> poi3 = new HashMap<>();
        poi3.put("id", "POI003");
        poi3.put("name", "富乐山公园");
        poi3.put("city", "绵阳");
        poi3.put("type", "attraction");
        poi3.put("coordinates", Map.of("lat", 31.456789, "lng", 104.756789));
        poi3.put("avg_visit_duration", 120); // 2小时
        poi3.put("opening_hours", Map.of("start", "06:00", "end", "22:00"));
        poi3.put("ticket_price", 0.0);
        poi3.put("popularity_score", 7.5);
        poiDatabase.add(poi3);
        
        Map<String, Object> poi4 = new HashMap<>();
        poi4.put("id", "POI004");
        poi4.put("name", "越王楼");
        poi4.put("city", "绵阳");
        poi4.put("type", "attraction");
        poi4.put("coordinates", Map.of("lat", 31.467890, "lng", 104.745678));
        poi4.put("avg_visit_duration", 90); // 1.5小时
        poi4.put("opening_hours", Map.of("start", "09:00", "end", "17:00"));
        poi4.put("ticket_price", 50.0);
        poi4.put("popularity_score", 7.0);
        poiDatabase.add(poi4);
        
        Map<String, Object> poi5 = new HashMap<>();
        poi5.put("id", "POI005");
        poi5.put("name", "仙海湖");
        poi5.put("city", "绵阳");
        poi5.put("type", "attraction");
        poi5.put("coordinates", Map.of("lat", 31.512345, "lng", 104.823456));
        poi5.put("avg_visit_duration", 210); // 3.5小时
        poi5.put("opening_hours", Map.of("start", "08:30", "end", "18:30"));
        poi5.put("ticket_price", 80.0);
        poi5.put("popularity_score", 8.5);
        poiDatabase.add(poi5);
        
        // 餐厅数据
        Map<String, Object> poi6 = new HashMap<>();
        poi6.put("id", "POI006");
        poi6.put("name", "绵阳米粉店");
        poi6.put("city", "绵阳");
        poi6.put("type", "restaurant");
        poi6.put("coordinates", Map.of("lat", 31.478901, "lng", 104.773456));
        poi6.put("avg_visit_duration", 60); // 1小时
        poi6.put("opening_hours", Map.of("start", "07:00", "end", "21:00"));
        poi6.put("ticket_price", 15.0);
        poi6.put("popularity_score", 8.5);
        poiDatabase.add(poi6);
        
        Map<String, Object> poi7 = new HashMap<>();
        poi7.put("id", "POI007");
        poi7.put("name", "川菜馆");
        poi7.put("city", "绵阳");
        poi7.put("type", "restaurant");
        poi7.put("coordinates", Map.of("lat", 31.481234, "lng", 104.775678));
        poi7.put("avg_visit_duration", 90); // 1.5小时
        poi7.put("opening_hours", Map.of("start", "11:00", "end", "21:00"));
        poi7.put("ticket_price", 60.0);
        poi7.put("popularity_score", 8.0);
        poiDatabase.add(poi7);
        
        Map<String, Object> poi8 = new HashMap<>();
        poi8.put("id", "POI008");
        poi8.put("name", "火锅店");
        poi8.put("city", "绵阳");
        poi8.put("type", "restaurant");
        poi8.put("coordinates", Map.of("lat", 31.476789, "lng", 104.771234));
        poi8.put("avg_visit_duration", 120); // 2小时
        poi8.put("opening_hours", Map.of("start", "11:00", "end", "22:00"));
        poi8.put("ticket_price", 100.0);
        poi8.put("popularity_score", 9.0);
        poiDatabase.add(poi8);
        
        return poiDatabase;
    }

    private static UserRequest getUserInput() {
        // 行程天数
        int days;
        while (true) {
            try {
                System.out.print("📅 请输入行程天数（如 3）: ");
                days = Integer.parseInt(scanner.nextLine().trim());
                if (days > 0) {
                    break;
                } else {
                    System.out.println("❌ 天数必须大于 0，请重新输入。");
                }
            } catch (NumberFormatException e) {
                System.out.println("❌ 请输入一个有效的整数。");
            }
        }

        // 必去地点
        System.out.print("📍 请输入必去地点（多个用英文逗号分隔，如：绵阳方特东方神画）: ");
        String mustVisitInput = scanner.nextLine().trim();
        List<String> mustVisit = mustVisitInput.isEmpty() ? new ArrayList<>() :
                Arrays.stream(mustVisitInput.split(","))
                        .map(String::trim)
                        .collect(Collectors.toList());

        // 禁止地点
        System.out.print("🚫 请输入禁止去的景点（多个用英文逗号分隔）: ");
        String forbiddenInput = scanner.nextLine().trim();
        List<String> forbidden = forbiddenInput.isEmpty() ? new ArrayList<>() :
                Arrays.stream(forbiddenInput.split(","))
                        .map(String::trim)
                        .collect(Collectors.toList());

        // 时间窗口
        System.out.println("🕒 请输入每日活动时间（格式 HH:MM，例如 09:00）");
        LocalTime startTime, endTime;
        while (true) {
            try {
                System.out.print("开始时间: ");
                startTime = LocalTime.parse(scanner.nextLine().trim(), timeFormatter);
                System.out.print("结束时间: ");
                endTime = LocalTime.parse(scanner.nextLine().trim(), timeFormatter);
                if (startTime.isBefore(endTime)) {
                    break;
                } else {
                    System.out.println("❌ 开始时间必须早于结束时间。");
                }
            } catch (DateTimeParseException e) {
                System.out.println("❌ 时间格式错误，请使用 HH:MM。");
            }
        }

        return new UserRequest(days, mustVisit, forbidden, startTime, endTime);
    }

    // 地理距离计算（Haversine 公式）
    private static double haversine(double lat1, double lon1, double lat2, double lon2) {
        double R = 6371.0; // 地球半径（公里）
        double phi1 = Math.toRadians(lat1);
        double phi2 = Math.toRadians(lat2);
        double deltaPhi = Math.toRadians(lat2 - lat1);
        double deltaLambda = Math.toRadians(lon2 - lon1);
        double a = Math.sin(deltaPhi / 2) * Math.sin(deltaPhi / 2) + 
                   Math.cos(phi1) * Math.cos(phi2) * Math.sin(deltaLambda / 2) * Math.sin(deltaLambda / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return R * c;
    }

    // 获取旅行时间（小时）
    private static double getTravelTime(double[] fromLocation, double[] toLocation) {
        double distance = haversine(fromLocation[0], fromLocation[1], toLocation[0], toLocation[1]);
        return distance / 10.0; // 假设平均速度 10 km/h
    }

    // 检查POI是否在营业时间内
    private static boolean isOpenAtTime(Map<String, Object> poi, LocalTime currentTime) {
        @SuppressWarnings("unchecked")
        Map<String, String> openingHours = (Map<String, String>) poi.get("opening_hours");
        if (openingHours == null) return true;
        
        LocalTime openTime = LocalTime.parse(openingHours.get("start"), timeFormatter);
        LocalTime closeTime = LocalTime.parse(openingHours.get("end"), timeFormatter);
        
        return !currentTime.isBefore(openTime) && !currentTime.isAfter(closeTime);
    }

    // 启发式评分函数
    private static double calculateHeuristicScore(Map<String, Object> poi, double[] currentLocation, LocalTime currentTime) {
        // 检查营业时间
        if (!isOpenAtTime(poi, currentTime)) {
            return Double.NEGATIVE_INFINITY;
        }
        
        // 获取坐标
        @SuppressWarnings("unchecked")
        Map<String, Double> coordinates = (Map<String, Double>) poi.get("coordinates");
        if (coordinates == null) return Double.NEGATIVE_INFINITY;
        
        double lat2 = coordinates.get("lat");
        double lon2 = coordinates.get("lng");
        double[] poiLocation = {lat2, lon2};
        
        // 计算旅行时间
        double travelTime = getTravelTime(currentLocation, poiLocation);
        if (travelTime > 2.0) { // 超过2小时不考虑
            return Double.NEGATIVE_INFINITY;
        }
        
        // 获取其他属性
        double popularityScore = (Double) poi.get("popularity_score");
        double ticketPrice = (Double) poi.get("ticket_price");
        
        // 启发式评分：score = (w1 * popularity_score) - (w2 * travel_time) - (w3 * ticket_price)
        double score = (W1_POPULARITY * popularityScore) - (W2_TRAVEL_TIME * travelTime) - (W3_TICKET_PRICE * ticketPrice);
        
        return score;
    }

    // 检查是否需要插入餐饮
    private static boolean needsMeal(LocalTime currentTime, List<Map<String, Object>> dayActivities) {
        // 检查午餐时间
        if (currentTime.isAfter(LUNCH_START) && currentTime.isBefore(LUNCH_END)) {
            boolean hasLunch = dayActivities.stream()
                    .anyMatch(activity -> "restaurant".equals(activity.get("type")) && 
                            activity.get("start_time") != null &&
                            LocalTime.parse((String) activity.get("start_time"), timeFormatter)
                                    .isAfter(LUNCH_START) &&
                            LocalTime.parse((String) activity.get("start_time"), timeFormatter)
                                    .isBefore(LUNCH_END));
            return !hasLunch;
        }
        
        // 检查晚餐时间
        if (currentTime.isAfter(DINNER_START) && currentTime.isBefore(DINNER_END)) {
            boolean hasDinner = dayActivities.stream()
                    .anyMatch(activity -> "restaurant".equals(activity.get("type")) && 
                            activity.get("start_time") != null &&
                            LocalTime.parse((String) activity.get("start_time"), timeFormatter)
                                    .isAfter(DINNER_START) &&
                            LocalTime.parse((String) activity.get("start_time"), timeFormatter)
                                    .isBefore(DINNER_END));
            return !hasDinner;
        }
        
        return false;
    }

    // 生成行程（第二阶段：启发式初始规划器）
    private static List<List<Map<String, Object>>> generateItinerary(UserRequest userRequest) {
        // 1. 初始化：创建空的行程计划
        List<List<Map<String, Object>>> plan = new ArrayList<>();
        for (int i = 0; i < userRequest.getDays(); i++) {
            plan.add(new ArrayList<>());
        }
        
        // 2. 确定每天的规划时间窗口
        LocalTime dailyStart = userRequest.getStartTime();
        LocalTime dailyEnd = userRequest.getEndTime();
        
        // 3. 处理必去地点和禁止地点
        List<Map<String, Object>> mustVisitList = new ArrayList<>();
        List<String> forbiddenList = userRequest.getForbidden();
        
        // 查找必去地点
        for (String mustVisitName : userRequest.getMustVisit()) {
            Optional<Map<String, Object>> found = pois.stream()
                    .filter(poi -> mustVisitName.equals(poi.get("name")))
                    .findFirst();
            if (found.isPresent()) {
                mustVisitList.add(found.get());
            } else {
                System.out.println("⚠️ 未找到必去地点: " + mustVisitName);
            }
        }
        
        // 4. 安插"必须访问"的地点（轮转分配）
        for (int i = 0; i < mustVisitList.size(); i++) {
            int dayIndex = i % userRequest.getDays();
            Map<String, Object> poi = mustVisitList.get(i);
            poi.put("start_time", dailyStart.format(timeFormatter));
            plan.get(dayIndex).add(poi);
        }
        
        // 5. 逐日填充行程
        for (int dayIndex = 0; dayIndex < userRequest.getDays(); dayIndex++) {
            List<Map<String, Object>> dayActivities = plan.get(dayIndex);
            double[] currentLocation = HOTEL_LOCATION.clone();
            LocalTime currentTime = dailyStart;
            
            // 更新当前位置为当天第一个活动的地点
            if (!dayActivities.isEmpty()) {
                Map<String, Object> first = dayActivities.get(0);
                @SuppressWarnings("unchecked")
                Map<String, Double> coordinates = (Map<String, Double>) first.get("coordinates");
                if (coordinates != null) {
                    currentLocation[0] = coordinates.get("lat");
                    currentLocation[1] = coordinates.get("lng");
                }
            }
            
            // 创建可用POI池（排除已安排和禁止的）
            List<Map<String, Object>> availablePois = pois.stream()
                    .filter(poi -> !forbiddenList.contains(poi.get("name")))
                    .filter(poi -> !dayActivities.contains(poi))
                    .filter(poi -> !mustVisitList.contains(poi))
                    .collect(Collectors.toList());
            
            // 贪心填充当天行程
            while (currentTime.isBefore(dailyEnd) && !availablePois.isEmpty()) {
                Map<String, Object> bestPoi = null;
                double bestScore = Double.NEGATIVE_INFINITY;
                
                // 检查是否需要插入餐饮
                boolean needMeal = needsMeal(currentTime, dayActivities);
                
                // 寻找下一个最佳活动
                for (Map<String, Object> poi : availablePois) {
                    // 如果需要餐饮，优先选择餐厅
                    if (needMeal && !"restaurant".equals(poi.get("type"))) {
                        continue;
                    }
                    
                    double score = calculateHeuristicScore(poi, currentLocation, currentTime);
                    if (score > bestScore) {
                        bestScore = score;
                        bestPoi = poi;
                    }
                }
                
                if (bestPoi == null) {
                    break;
                }
                
                // 计算时间消耗
                @SuppressWarnings("unchecked")
                Map<String, Double> coordinates = (Map<String, Double>) bestPoi.get("coordinates");
                double[] poiLocation = {coordinates.get("lat"), coordinates.get("lng")};
                double travelTime = getTravelTime(currentLocation, poiLocation);
                int visitDuration = (Integer) bestPoi.get("avg_visit_duration");
                double totalTimeHours = travelTime + (visitDuration / 60.0);
                
                // 检查是否会超时
                LocalTime endTime = currentTime.plusMinutes((long) (totalTimeHours * 60));
                if (endTime.isAfter(dailyEnd)) {
                    break;
                }
                
                // 添加到行程
                bestPoi.put("start_time", currentTime.format(timeFormatter));
                dayActivities.add(bestPoi);
                availablePois.remove(bestPoi);
                
                // 更新时间和位置
                currentTime = endTime;
                currentLocation[0] = poiLocation[0];
                currentLocation[1] = poiLocation[1];
            }
        }
        
        return plan;
    }

    // 输出行程
    private static void printItinerary(List<List<Map<String, Object>>> itinerary) {
        System.out.println("\n🎉 生成的行程安排如下：\n");
        for (int dayIndex = 0; dayIndex < itinerary.size(); dayIndex++) {
            List<Map<String, Object>> dayActivities = itinerary.get(dayIndex);
            
            System.out.println("🗓️  DAY " + (dayIndex + 1) + ":");
            if (dayActivities.isEmpty()) {
                System.out.println("   (无安排)");
            } else {
                for (int i = 0; i < dayActivities.size(); i++) {
                    Map<String, Object> activity = dayActivities.get(i);
                    String name = (String) activity.get("name");
                    String type = (String) activity.get("type");
                    String startTime = (String) activity.get("start_time");
                    double price = (Double) activity.get("ticket_price");
                    double popularity = (Double) activity.get("popularity_score");
                    
                    String typeIcon = "restaurant".equals(type) ? "🍽️" : "🏛️";
                    System.out.printf("   %d. %s %s | ⏰%s | 💰%.0f | 🔥%.1f%n", 
                            i + 1, typeIcon, name, startTime, price, popularity);
                }
            }
            System.out.println();
        }
    }
}
